﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Web.UI;
using System.Globalization;

namespace Noftware.SPArchiveTweets.TweetTimer
{
    /// <summary>
    /// SharePoint extension methods.
    /// </summary>
    public static class SPExtensions
    {
        /// <summary>
        /// Rename (repurpose) the built-in Title field.
        /// </summary>
        /// <param name="list">List containing the title field.</param>
        /// <param name="newName">New name.</param>
        public static void RenameTitleField(this SPList list, string newName)
        {
            var titleField = list.Fields["Title"];
            titleField.Title = newName;
            titleField.TitleResource.SetValueForUICulture(new CultureInfo(1033), newName);
            titleField.Update(true);
        }

        /// <summary>
        /// Deletes all lookup columns.
        /// </summary>
        /// <param name="spList">List.</param>
        /// <param name="lookupColumnName">Name of column to delete.</param>
        public static void DeleteLookupColumns(this SPList spList)
        {
            var lookupFieldNames = new List<string>();
            foreach (SPField field in spList.Fields)
            {
                if (field is SPFieldLookup)
                {
                    // Store name of field to deleted
                    if (field.CanBeDeleted == true)
                    {
                        lookupFieldNames.Add(field.Title);
                    }
                }
            } // foreach (SPField field in spL...

            // Iterate over all fields to be deleted
            foreach (string lookupFieldName in lookupFieldNames)
            {
                // Get field and deleted
                var field = spList.Fields[lookupFieldName];
                field.Delete();
            }

            // If fields were deleted, update list
            if (lookupFieldNames.Count > 0)
            {
                spList.Update();
            }
        }

        /// <summary>
        /// Add a lookup column to a child list that links to a column in the parent list.
        /// </summary>
        /// <param name="spChildList">Child list.</param>
        /// <param name="spParentList">Parent list.</param>
        /// <param name="parentColumnName">Parent column name (i.e. primary key).</param>
        /// <param name="childColumnName">Child column name (i.e. foreign key).</param>
        public static void AddLookupColumn(this SPList spChildList, SPList spParentList, string parentColumnName, string childColumnName)
        {
            string fkColumnName = spChildList.Fields.AddLookup(childColumnName, spParentList.ID, true);
            var fkColumn = (SPFieldLookup)spChildList.Fields.GetFieldByInternalName(fkColumnName);
            fkColumn.LookupField = spParentList.Fields[parentColumnName].InternalName;
            fkColumn.Indexed = true;
            fkColumn.RelationshipDeleteBehavior = SPRelationshipDeleteBehavior.Restrict;
            fkColumn.Update();
            spChildList.Update();
        }

        /// <summary>
        /// Wrapper around SPWeb.DoesUserHavePermissions with CatchAccessDeniedException set to false;
        /// </summary>
        /// <param name="web">SPWeb reference.</param>
        /// <param name="permissionMask">Permission to check.</param>
        /// <returns>True if current user has permission, false if otherwise.</returns>
        public static bool DoesUserHavePermissions(this SPWeb web, SPBasePermissions permissionMask)
        {
            bool catchException = SPSecurity.CatchAccessDeniedException;
            SPSecurity.CatchAccessDeniedException = false;
            try
            {
                bool permission = web.DoesUserHavePermissions(permissionMask);
                return permission;
            }
            catch (Exception)
            {
                return false;
            }
            finally
            {
                // Reset the flag to original value
                SPSecurity.CatchAccessDeniedException = catchException;
            }
        }

        /// <summary>
        /// Render JavaScript to the page.
        /// </summary>
        /// <param name="page">Reference to page.</param>
        /// <param name="js">JavaSCript to render. Do not include script tags.</param>
        private static void renderJavaScript(this Page page, string js)
        {
            if (page == null)
            {
                return;
            }

            string key = System.Guid.NewGuid().ToString();

            // http://spblog.net/post/2012/01/20/SharePoint-2010-Script-On-Demand%E2%80%93give-my-script-right-now!.aspx
            page.ClientScript.RegisterStartupScript(page.GetType(), key, js, true);
        }

        // http://www.west-wind.com/weblog/posts/114530.aspx
        /// <summary>
        /// Encodes a string to be represented as a string literal. The format
        /// is essentially a JSON string.
        /// 
        /// The string returned includes outer quotes 
        /// Example Output: "Hello \"Rick\"!\r\nRock on"
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string EncodeJsString(this string s)
        {
            var sb = new StringBuilder();
            //sb.Append("\"");
            foreach (char c in s)
            {
                switch (c)
                {
                    case '\'':
                        sb.Append("\\\'");
                        break;
                    case '\"':
                        sb.Append("\\\"");
                        break;
                    case '\\':
                        sb.Append("\\\\");
                        break;
                    case '\b':
                        sb.Append("\\b");
                        break;
                    case '\f':
                        sb.Append("\\f");
                        break;
                    case '\n':
                        sb.Append("\\n");
                        break;
                    case '\r':
                        sb.Append("\\r");
                        break;
                    case '\t':
                        sb.Append("\\t");
                        break;
                    default:
                        int i = (int)c;
                        if (i < 32 || i > 127)
                        {
                            sb.AppendFormat("\\u{0:X04}", i);
                        }
                        else
                        {
                            sb.Append(c);
                        }
                        break;
                }
            }
            //sb.Append("\"");

            return sb.ToString();
        }

        /// <summary>
        /// Render JavaScript confirmation function to the page.
        /// </summary>
        /// <param name="page">Reference to page.</param>
        /// <param name="message">Dialog message.</param>
        public static void RenderJsConfirmation(this Page page, string message)
        {
            if (message != null)
            {
                message = message.EncodeJsString();
            }

            const string jsMsg = "return confirmation('{0}');";
            renderJavaScript(page, string.Format(jsMsg, message));
        }

        /// <summary>
        /// Render JavaScript to the page.
        /// </summary>
        /// <param name="page">Reference to page.</param>
        /// <param name="title">Dialog title.</param>
        /// <param name="message">Dialog message.</param>
        public static void RenderSPDialogMessageBox(this Page page, string title, string message)
        {
            if (title != null)
            {
                title = title.EncodeJsString();
            }
            if (message != null)
            {
                message = message.EncodeJsString();
            }

            const string jsMsg = "displayModalMessage('{0}', '{1}');";
            renderJavaScript(page, string.Format(jsMsg, title, message));
        }

        // http://splog.cairo.ag/2010/12/28/setting-the-value-of-lookup-fields-in-c/
        /// <summary>
        /// Get a reference to the parent list's lookup value based on field name and field value.
        /// </summary>
        /// <param name="parentList">Reference to parent list.</param>
        /// <param name="fieldName">Name of field.</param>
        /// <param name="lookupValue">Value of field.</param>
        /// <returns>Populated SPFieldLookupValue.</returns>
        public static SPFieldLookupValue GetLookupValue(this SPList parentList, string fieldName, string lookupValue)
        {
            // Replace spaces with x0020
            if (fieldName.Contains(" ") == true)
            {
                fieldName = fieldName.Replace(" ", "_x0020_");
            }
            string caml = string.Format("<Where><Eq><FieldRef Name=\"{0}\" /><Value Type=\"Text\">{1}</Value></Eq></Where>", fieldName, lookupValue);

            var query = new Microsoft.SharePoint.SPQuery()
            {
                Query = caml
            };
            var listItems = parentList.GetItems(query);

            return new SPFieldLookupValue(listItems[0].ID.ToString());
        }
    }
}
